package cn.itcast.zt.java8.cut_point;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.itcast.zt.java8.algorithm.IntervalHitUtil;
import cn.itcast.zt.java8.algorithm.MurmurHash;

import java.util.*;

/**
 * 校验切量命中情况
 * 进入到判断逻辑了前提条件就是必须得保证参数有值，没有值在前置自行处理拦截掉
 * 这里面配置的比较区域的大小均为前闭后开的区间[,)
 * @author zhangtian105
 * @date 2024/1/15
 * @apiNote
 */
public class CutPointCheckUtil {
    public static final String CLIENT_WH5 = "wh5";
    public static final String CLIENT_H5 = "h5";
    public static final String DEFAULT_KEY = "default" ;

    /**
     * 返回值true 表示切量以及白名单通过 直接放行
     * 返回false 表示切量未切中及黑名单拦截，直接拦截
     * @param baseWhiteBlackList
     * @param cutPoint
     * @param inParams
     * @param key
     * @return
     */
    public static final Boolean checkCutPoint(BaseInParams inParams, BaseWhiteBlackList baseWhiteBlackList, BaseGoupReleaseCutPoint cutPoint, String key) {
        if (StrUtil.isBlank(key)) {
            key = DEFAULT_KEY ;
        }

        // 参数校验 不通过则拦截
        if (Objects.isNull(inParams) || Objects.isNull(cutPoint)) {
            return false ;
        }

        // h5或小程序直接放行
        if (StrUtil.equals(CLIENT_H5, inParams.getClient()) || StrUtil.equals(CLIENT_WH5, inParams.getClient())) {
            return true ;
        }

        // 获取需要切量的分组（可以是不同的业务接入）
        TreeMap<String, BaseCutPoint> pointTreeMap = cutPoint.getGroupReleaseTimeCutPoint().get(key) ;
        if (MapUtil.isEmpty(pointTreeMap)) {
            return false ;
        }

        // TreeMap降序在初始化时设定
        /*TreeMap<Long, BaseCutPoint> a = new TreeMap<>(Collections.reverseOrder()) ;
        TreeMap<Long, BaseCutPoint> b = new TreeMap<>(new Comparator<Long>() {
            // 降序排序
            @Override
            public int compare(Long o1, Long o2) {
                return o2.compareTo(o1);
            }
        }) ;*/


        // 获取当前切量命中的时间戳
        Long currentTime = System.currentTimeMillis() ;
        // 获取生效的区间
        BaseCutPoint baseCutPoint = pointTreeMap
                                        .entrySet()
                                        .stream()
                                        .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
                                        .filter(p -> currentTime >= Long.valueOf(p.getKey()))
                                        .findFirst()
                                        .orElse(null)
                                        .getValue() ;

        // 未配置切量
        if (Objects.isNull(baseCutPoint)) {
            return false ;
        }

        // 校验客户端、版本、设备号
        if (StrUtil.isNotBlank(inParams.getClient())
                && StrUtil.isNotBlank(inParams.getClientVersion())
                && StrUtil.isNotBlank(inParams.getUuid())
                && baseCutPoint.getClient().contains(inParams.getClient())) {

            // 进入该内部逻辑后，版本号优先级最高 先处理版本号，版本号不符合的return false
            String clientVersionStart = baseCutPoint.getClientVersionStart();
            String clientVersionEnd = baseCutPoint.getClientVersionEnd();

            // 执行区间转换 12.3.4 -> 120304
            Integer exchangeClientVersion = IntervalHitUtil.exchangeClientVersion(inParams.getClientVersion()) ;

            Integer exchangeClientVersionStart = Integer.MIN_VALUE ;
            Integer exchangeClientVersionEnd = Integer.MAX_VALUE ;

            if (StrUtil.isNotBlank(clientVersionStart)) {
                exchangeClientVersionStart = IntervalHitUtil.exchangeClientVersion(clientVersionStart) ;
            }

            if (StrUtil.isNotBlank(clientVersionEnd)) {
                exchangeClientVersionEnd = IntervalHitUtil.exchangeClientVersion(clientVersionEnd) ;
            }

            // 比较版本
            if (exchangeClientVersion >= exchangeClientVersionStart && exchangeClientVersion < exchangeClientVersionEnd) {
                // 拿出黑白名单的用户
                List<String> whiteList = baseWhiteBlackList.getWhiteList();
                List<String> blackList = baseWhiteBlackList.getBlackList();

                // pin为空业务前置自行判断
                // 这里设定黑名单优先
                if (CollUtil.isNotEmpty(blackList) && blackList.contains(inParams.getPin())) {
                    return false ;
                }

                if (CollUtil.isNotEmpty(whiteList) && whiteList.contains(inParams.getPin())) {
                    return true ;
                }

                int percentStart = baseCutPoint.getPercentStart() == null ? -1 : baseCutPoint.getPercentStart() ;
                int percentEnd = baseCutPoint.getPercentEnd() == null ? 0 : baseCutPoint.getPercentEnd() ;

                // 判断是否在开放的比例范围内
                int hashVal = calculateRate(inParams, 0) ;
                if (hashVal < percentEnd && hashVal >= percentStart) {
                    return true;
                }
            }
        }

        return false ;
    }

    public static int calculateRate(BaseInParams inParams, long seed) {
        int mod;
        String uuid = inParams.getUuid();
        if (StrUtil.isBlank(uuid)) {
            Random r = new Random(seed);
            mod = r.nextInt(100);
        } else {
            int deviceId = Math.abs(MurmurHash.hash(uuid));
            mod = deviceId % 100;
        }

        return mod;
    }
}
